﻿using System;
using System.Device.Location;
using System.Linq;
using System.Net;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using Tequila.Phone.Location.Bing.DTO;

namespace Tequila.Phone.Location.Bing
{
    public class BingGeoLocationService: IBingGeoLocationService
    {
        private const string bingServiceBaseUrlFormat = "http://dev.virtualearth.net/REST/v1/Locations?key={0}&q={1}&inclnb=1";

        public void Search(string apiKey, string culture, string query, Action<GeoLocation> callback)
        {
            if (string.IsNullOrEmpty(apiKey))
            {
                throw new BingServiceException("Key can't be empty");
            }

            if (string.IsNullOrEmpty(culture))
            {
                throw new BingServiceException("Culture param can't be empty");
            }

            if (string.IsNullOrEmpty(query))
            {
                throw new BingServiceException("Query can't be empty");
            }

            try
            {
                var webClient = new WebClient();
                webClient.OpenReadCompleted += (sender, args) => ProcessResponse(args, callback);
                webClient.OpenReadAsync(new Uri(string.Format(bingServiceBaseUrlFormat, apiKey, query)));
            }
            catch (Exception e)
            {
                callback(GeoLocation.Empty);
                throw new BingServiceException("Unable send request to server", e);
            }
        }

        private void ProcessResponse(OpenReadCompletedEventArgs args, Action<GeoLocation> callback)
        {
            var location = GeoLocation.Empty;

            try
            {
                if (args.Error != null)
                {
                    throw new BingServiceException("Unable process server response", args.Error); 
                }

                var response = DeserializeResponse(args);
                if (response.IsSuccess)
                {
                    location = ParseGeoLocation(response);
                }
            }
            catch(SerializationException e)
            {
               throw new BingServiceException("Unable process server response", e); 
            }
            finally
            {
                callback(location);
            }
        }

        private static BingResponse DeserializeResponse(OpenReadCompletedEventArgs args)
        {
            var serializer = new DataContractJsonSerializer(typeof (BingResponse));
            var response = serializer.ReadObject(args.Result) as BingResponse;
            return response;
        }

        private static GeoLocation ParseGeoLocation(BingResponse response)
        {
            GeoLocation location = GeoLocation.Empty;

            if (response.ResourceSets.Any())
            {
                var foundResources = response.ResourceSets[0];
                if (foundResources.Resources.Any())
                {
                    var geoResource = foundResources.Resources[0];
                    var point = geoResource.Point;
                    var geoCoordinate = new GeoCoordinate(point.Coordinates[0], point.Coordinates[1]);
                    location = new GeoLocation(geoResource.Name, geoCoordinate);
                }
            }

            return location;
        }
    }
}
